iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Modern Web

《NestJS 絕地求生手冊》:我用一年血淚換來的實戰排雷筆記系列 第 2

Day 2|消失的提供者:為什麼寫了 Service 卻噴 can't resolve dependencies?

  • 分享至 

  • xImage
  •  

在 NestJS 中,DI(Dependency Injection,依賴注入)是整個框架的核心機制,開發者只要在 constructor 寫一行 private readonly postService: PostService,框架就會自動幫你注入實例——聽起來很神奇,但這套機制可不是無條件運作的。

今天我們就來拆解三個初學者最容易踩的 DI 陷阱。

陷阱一:忘記將 Service 放入 providers 陣列

當你在 Controller 的建構子中注入了 service:

@Controller('post')
export class PostController {
  constructor(private readonly postService: PostService) {}
}

但忘記在對應的 Module 中宣告它:

@Module({
  // ❌ 陷阱:PostController 注入了 PostService,但未加入 providers 中
  // providers: [PostService],
  controllers: [PostController],
})
export class PostModule {}

此時會出現錯誤:

[Error]: Nest cannot export a provider/module that is not a part of the currently processed module (PostModule). Please verify whether the exported PostService is available in this particular context.

Possible Solutions:
-Is PostService part of the relevant providers/imports within PostModule?

陷阱二:跨模組注入時,忘記匯出 Service

如果 UserService 想要使用 PostService 提供的共用邏輯:

@Injectable()
export class UserService {
  constructor(private readonly postService: PostService) {}
}

即使我們在 UserModule 中匯入了 PostModule,但如果 PostModule 沒有把 PostService 匯出,一樣會報錯:

@Module({
  imports: [PostModule],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
@Module({
  providers: [PostService], 
  controllers: [PostController],
  // ❌ 陷阱:UserService 欲使用 PostService,但此處未匯出
})
export class PostModule {}
[Nest] 16311  - 09/16/2026, 5:25:28 PM   ERROR [ExceptionHandler] UnknownDependenciesException [Error]: Nest can't resolve dependencies of the PostController (?). Please make sure that the argument PostService at index [0] is available in the PostModule module.

Potential solutions:
- Is PostModule a valid NestJS module?
- If PostService is a provider, is it part of the current PostModule?
- If PostService is exported from a separate @Module, is that module imported within PostModule?
  @Module({
    imports: [ /* the Module containing PostService */ ]
  })

陷阱三:忘記 imports 依賴模組

接續上面,假設 PostModule 已經匯出了 PostService,但作為需求方模組的 UserModule 卻忘記引入它:

@Module({
  // ❌ 陷阱:UserService 注入了 PostService,但未匯入提供它的 PostModule
  // imports: [PostModule], 
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
ERROR [ExceptionHandler] UnknownDependenciesException [Error]: Nest can't resolve dependencies of the UserService (?). Please make sure that the argument PostService at index [0] is available in the UserModule module.

Potential solutions:
-Is UserModule a valid NestJS module?
-If PostService is a provider, is it part of the current UserModule?
-If PostService is exported from a separate @Module, is that module imported within UserModule?
  @Module({
    imports: [ /* the Module containing PostService */ ]
  })

根因:Provider 預設被 Module 封裝,DI 只解析目前 Context 可見的依賴

當你寫下:

@Injectable()
export class UserService {
  constructor(private readonly postService: PostService) {}
}

NestJS 其實不會去全域搜尋全專案哪裡有 PostService 這個 class。它只會在意一件事:「在 UserModule 當前能管轄的範圍內,到底知不知道 PostService 是從哪來的?」

Nest 會根據 @Module() 中的 metadata 建立模組與 provider 之間的關係。當它建立 controller 或 provider 實例時,會解析 constructor 所需要的每一個 dependency。

UserService 為例,Nest 需要找到 PostService

在一般的 Module 情境下,UserModule 能使用的 provider 主要來自兩個地方:

  1. UserModule 自己 providers 中註冊的 provider
  2. UserModuleimports 所引入之模組,透過 exports 公開出來的 provider

這也正好對應 @Module() 中三個最重要的陣列:

屬性 白話理解 作用
providers 我自己有哪些東西 註冊由本模組管理的 provider
exports 我願意公開哪些東西 將 provider 公開給其他模組使用
imports 我要接上哪些模組 取得其他模組所公開的 provider

所以這三個陷阱,其實不是三套不同規則,而是同一條 provider 可見性路徑斷在不同位置

陷阱 斷點 結果
陷阱一 PostService 沒有註冊進 PostModule.providers PostModule 內部自己就找不到 PostService
陷阱二 PostModule 沒有 exports: [PostService] PostService 存在,但被封裝在模組內,沒有對外公開
陷阱三 UserModule 沒有 imports: [PostModule] PostService 已對外公開,但 UserModule 尚未建立連線

排雷指南

當你再遇到 Nest can't resolve dependencies...,請不要慌張,跟著這三步循序排查:

Step 1:查錯誤來源

先看終端機後半句:哪個 Controller / Service 缺少了哪個依賴?available in the XXX module 寫的是哪個模組?

Step 2:查同模組

缺少的 service 是否在該模組的 providers 陣列中?(若是控制器缺 Service,看它自己的 Module)

Step 3:查跨模組

  • 提供方模組:提供 service 的模組是否有寫 exports: [XxxService]
  • 需求方模組:使用 service 的模組是否有寫 imports: [XxxModule]

只要掌握這三個步驟,就能排除很大一部分常見的 NestJS DI 啟動錯誤。

總結

  1. 同模組注入:先確認需要的 provider 是否已註冊在 providers 中。
  2. 跨模組注入:確認提供方有 exports,需求方有 imports
  3. 錯誤訊息判讀:先看誰缺誰,以及在哪個 Module context 找不到。

參考資料


上一篇
Day 1|前言
下一篇
Day 3|消失的 IoC 控制權:為什麼手動 new Service() 會繞過 NestJS 的 DI?
系列文
《NestJS 絕地求生手冊》:我用一年血淚換來的實戰排雷筆記6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言